# ReAct 框架 Reasoning + Acting
import os
from langchain_ollama import ChatOllama
from langchain_tavily import TavilySearch
from langchain.schema import SystemMessage
# 注意：虽然initialize_agent已被弃用，但为了兼容性暂时保留
from langchain.agents import AgentType, initialize_agent

os.environ["TAVILY_API_KEY"] = "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"

# 初始化模型
llm = ChatOllama(
    model="deepseek-r1:8b",
    base_url="http://localhost:11434",
    temperature=0.5,
    num_predict=512
)

# Tavily 搜索工具（使用推荐的方式）
search_tool = TavilySearch(
    name="web_search",
    description="搜索网络获取最新信息，适用于新闻、天气、事实查询等",
    max_results=2  # 对小模型来说，结果少一些更容易处理
)

tools = [search_tool]

# 系统提示词优化
system_message = SystemMessage(
    content="""你是一个有帮助的AI助手，可以访问网络搜索工具。
      使用指南：
      1. 当需要最新信息时，使用搜索工具
      2. 思考过程要简洁明了
      3. 搜索后，总结关键信息回答用户
      4. 如果搜索结果显示多个来源，请综合信息给出平衡的观点
      请用中文思考和回答。"""
)
    
# 初始化 Agent - 使用支持多输入工具的类型
# AgentExecutor 是 Agent 的执行器，负责根据用户输入调用 Agent 并返回结果
# AgentExecutor类作为链（Chain）而存在，同时也为代理执行各种工具，完成任务。它会接收代理的计划，并执行代理思考链路中每一步的行动。
# 2023 年初，LangChain 引入了“多操作”代理框架，允许代理计划执行多个操作。在此基础上，LangChain 推出了结构化工具对话代理，允许更复杂、多方面的交互。通过指定 AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION 这个代理类型，代理能够调用包含一系列复杂工具的“结构化工具箱”，组合调用其中的多个工具，完成批次相关的任务集合。
agent = initialize_agent(
    tools=tools,
    llm=llm,
    # 教程中此处使用更为简单的代理：AgentType.ZERO_SHOT_REACT_DESCRIPTION
    agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION,  # 支持多输入工具，组合调用多个工具
    verbose=True,
    handle_parsing_errors=True,
    max_iterations=4,
    # 对于此类型的Agent，我们使用不同的参数方式
    agent_kwargs={
      "system_message": system_message.content
    },
    early_stopping_method="generate"
)

# 对比只通过LLM回答案例，ReAct 框架未出现幻觉
# ReAct = Reasoning + Acting
response = agent.invoke({"input": '唐代有个诗人叫鲁迅，他写了一首诗叫《悯农》，你能背诵下吗？'})
print('response:', response.get('output', '无回答内容'))
